
#include <spu_intrinsics.h>
#include <spu_mfcio.h>
//#include <libspe2.h>
#include "cmatrixprod.h"


/* Variables Globales */

t_complex bloc_a[BLOC_SIZE*BLOC_SIZE] __attribute__((aligned(16)));
t_complex bloc_b[BLOC_SIZE*BLOC_SIZE] __attribute__((aligned(16)));
t_complex bloc_c[BLOC_SIZE*BLOC_SIZE] __attribute__((aligned(16)));
t_complex buff_a[BLOC_SIZE*BLOC_SIZE] __attribute__((aligned(16)));
t_complex buff_b[BLOC_SIZE*BLOC_SIZE] __attribute__((aligned(16)));

abs_params_t abs_params __attribute__((aligned(16)));

int step,spe_num,tid = 0, rid = 0, tag = 1;
uint32_t tag_mask=1<<1;
unsigned int res;
uint64_t next_addr;


/* Fonction d'affichage */
void affiche(t_complex a[], int nlig, int ncol, char *s){
  int i,j;
  printf("\n__SPE\n%s=[\n",s);
  for (i=0; i<nlig;i++) {
    for (j=0;j<ncol;j++) 
      printf("%f + %f*i  ",a[i*ncol+j].r,a[i*ncol+j].i);
    printf(";\n"); 
  }   
 printf("];\n");
}


/* Fonction de calculation de multiplication complexe */
void calculation(int M, int N, int P, t_complex *A, 
		 t_complex *B, t_complex *C){
  int i,j,k;
  t_complex som;
  for(i=0;i<M;i++){
    for (j=0;j<N;j++){
      som=C[i*N+j];
      for (k=0;k<P;k++) {
	som.r = som.r + A[i*P+k].r*B[k*N+j].r-A[i*P+k].i*B[k*N+j].i;
	som.i = som.i + A[i*P+k].r*B[k*N+j].i+A[i*P+k].i*B[k*N+j].r;
      }
      C[i*N+j]=som;
    }
  }
}

void load_up(){

  int i;
  for (i=0; i<BLOC_SIZE; i++){
    mfc_get(bloc_a + i*BLOC_SIZE,
	    abs_params.in_matrix_A + abs_params.size_M*i + BLOC_SIZE * spe_num, 
	    BLOC_SIZE*sizeof(t_complex),
	    tag, tid, rid);
    mfc_get(bloc_b + i*BLOC_SIZE,
	    abs_params.in_matrix_B + abs_params.size_N*i + BLOC_SIZE * spe_num + abs_params.size_N * spe_num,
	    BLOC_SIZE*sizeof(t_complex),
	    tag, tid, rid);
    mfc_get(bloc_c + i*BLOC_SIZE,
	    abs_params.in_matrix_B + abs_params.size_N*i + BLOC_SIZE * spe_num + abs_params.size_N * spe_num,
	    BLOC_SIZE*sizeof(t_complex),
	    tag, tid, rid);
    mfc_write_tag_mask(tag_mask);
    res = mfc_read_tag_status_all();
  }
}

void load_up_iter(){

  int i;
  for (i=0; i<BLOC_SIZE; i++){
    mfc_get(bloc_a + i*BLOC_SIZE,
	    abs_params.in_matrix_A + abs_params.size_M*i + BLOC_SIZE * spe_num, 
	    BLOC_SIZE*sizeof(t_complex),
	    tag, tid, rid);
    mfc_get(bloc_b + i*BLOC_SIZE,
	    abs_params.in_matrix_B + abs_params.size_N*i + BLOC_SIZE * spe_num + abs_params.size_N * spe_num,
	    BLOC_SIZE*sizeof(t_complex),
	    tag, tid, rid);
    mfc_get(bloc_c + i*BLOC_SIZE,
	    abs_params.in_matrix_B + abs_params.size_N*i + BLOC_SIZE * spe_num + abs_params.size_N * spe_num,
	    BLOC_SIZE*sizeof(t_complex),
	    tag, tid, rid);
    mfc_write_tag_mask(tag_mask);
    res = mfc_read_tag_status_all();
  }
}

void load_bloc_a(int n){

  int i;
  for (i=0; i<BLOC_SIZE; i++){
    mfc_get(bloc_a + i*BLOC_SIZE,
	    abs_params.in_matrix_A + abs_params.size_M*i + BLOC_SIZE * spe_num + abs_params.size_M * BLOC_SIZE * n,
	    BLOC_SIZE*sizeof(t_complex),
	    tag, tid, rid);
    mfc_write_tag_mask(tag_mask);
    res = mfc_read_tag_status_all();

    printf("__SPE %d did load_bloc_a(%d) - line %d\n",spe_num,n,i);

  }
}

void load_bloc_b(int n){
  
  int i;
  for (i=0; i<BLOC_SIZE; i++){
    mfc_get(bloc_b + i*BLOC_SIZE,
	    abs_params.in_matrix_B  + BLOC_SIZE * spe_num + 
	    (abs_params.size_N*i + abs_params.size_N * BLOC_SIZE * n + abs_params.size_N * BLOC_SIZE * spe_num) % 
	    (abs_params.size_N * abs_params.size_N),
	    BLOC_SIZE*sizeof(t_complex),
	    tag, tid, rid);
    mfc_write_tag_mask(tag_mask);
    res = mfc_read_tag_status_all();
  }
}



void get_addr(){
  uint32_t adrl,adrh;
  /*  SEND BUFF ADDR  */
  while (spu_stat_out_mbox() == 0);
  spu_write_out_mbox((uint32_t) buff_a); // TODO ??
  /*  RECV BUFF ADDR  */
  while (spu_stat_in_mbox() == 0);
  adrh = spu_read_in_mbox();
  while (spu_stat_in_mbox() == 0);
  adrl = spu_read_in_mbox();
  next_addr = mfc_hl2ea(adrh,adrl);
}


int main(unsigned long long spe, unsigned long long argp, unsigned long long envp)
{

  int i,j,u,k,l,m,n,sig;
  t_complex som;
  uint32_t adrl,adrh;

  /* SEND & RECV ADDRESSES */
  //get_addr();
  /*  SEND BUFF ADDR  */
  while (spu_stat_out_mbox() == 0);
  spu_write_out_mbox((uint32_t) buff_a); // TODO ??
  /*  RECV BUFF ADDR  */
  while (spu_stat_in_mbox() == 0);
  adrh = spu_read_in_mbox();
  while (spu_stat_in_mbox() == 0);
  adrl = spu_read_in_mbox();
  next_addr = mfc_hl2ea(adrh,adrl);




  
  /* DMA Transfer 1 : GET input/output parameters */
  mfc_get(&abs_params, argp, sizeof(abs_params_t), tag, tid, rid);
  mfc_write_tag_mask(tag_mask);
  res = mfc_read_tag_status_all();

  spe_num = abs_params.spe_num;

  printf("num_spe : %d, next_spe : %llx\n",spe_num,next_addr);

  /*  LOAD INITIAL VALUES  */
  //load_up();

  //affiche(bloc_a, BLOC_SIZE, BLOC_SIZE, "test");
  

  


  /***************************
   *      MAIN ALGORITHM     *
   ***************************/
  
  for (i=0; i<NUM_SPE; i++){



    //if ( (spe_num != 0) && (spe_num != 1) ){
    if ( spe_num != 0 ){
      while(1){
      }
    }
    // TEST
    //if (spe_num == 0){
    

    printf("__SPE %d - Init (i,j) = (%d,0)\n",spe_num,i);

    
    /* load from A(i,0) */
    //load_bloc_a(i);
    for (u=0; u<BLOC_SIZE; u++){

      //TEST VAL
      i = NUM_SPE-1;

      mfc_get(bloc_a + u*BLOC_SIZE,
	      abs_params.in_matrix_A + 
	      (unsigned long long)(abs_params.size_M*u + BLOC_SIZE * spe_num + abs_params.size_M * BLOC_SIZE * i),
	      BLOC_SIZE*sizeof(t_complex),
	      tag, tid, rid);
      mfc_write_tag_mask(tag_mask);
      res = mfc_read_tag_status_all();
      printf("__SPE %d did load_bloc_a(%d) - line %d\n",spe_num,i,u);
    }
    affiche(bloc_a, BLOC_SIZE, BLOC_SIZE, "test de *****");


    


    for (j=0; j<NUM_SPE; j++){
 


      printf("__SPE %d (i,j) = (%d,%d)\n",spe_num,i,j);
      /* load from B(j+spe_num,spe_num) */
      //load_bloc_b(j);
      for (k=0; k<BLOC_SIZE; k++){
	mfc_get(bloc_b + k*BLOC_SIZE,
		abs_params.in_matrix_B  + BLOC_SIZE * spe_num + 
		(abs_params.size_N*k + abs_params.size_N * BLOC_SIZE * j + abs_params.size_N * BLOC_SIZE * spe_num) % 
		(abs_params.size_N * abs_params.size_N),
		BLOC_SIZE*sizeof(t_complex),
		tag, tid, rid);
	mfc_write_tag_mask(tag_mask);
	res = mfc_read_tag_status_all();
      }





      printf("__SPE %d - A\n",spe_num);
      
      /* bloc_c() = bloc_c() + bloc_a * bloc_b */
      //calculation(BLOC_SIZE, BLOC_SIZE, BLOC_SIZE, bloc_a, bloc_b, bloc_c);
      for(l=0;l<BLOC_SIZE;l++){
	for (m=0;m<BLOC_SIZE;m++){
	  som=bloc_c[l*BLOC_SIZE+m];
	  for (n=0;n<BLOC_SIZE;n++) {
	    som.r = som.r + bloc_a[l*BLOC_SIZE+n].r*bloc_b[n*BLOC_SIZE+m].r-bloc_a[l*BLOC_SIZE+n].i*bloc_b[n*BLOC_SIZE+m].i;
	    som.i = som.i + bloc_a[l*BLOC_SIZE+n].r*bloc_b[n*BLOC_SIZE+m].i+bloc_a[l*BLOC_SIZE+n].i*bloc_b[n*BLOC_SIZE+m].r;
	  }
	  bloc_c[l*BLOC_SIZE+m]=som;
	}
      }






      printf("__SPE %d - B\n",spe_num);
      /* read from SPEi+1 into buff */  // TODO : PUT buff_a INTO 64 bit???  WHAT SIZE FOR PUT ???
      mfc_put(bloc_a, next_addr, BLOC_SIZE * BLOC_SIZE * sizeof(t_complex), tag, tid, rid);
      //mfc_put(bloc_a, next_addr, BLOC_SIZE * sizeof(t_complex), tag, tid, rid);
	      



      printf("__SPE %d - C\n",spe_num);
      /* Sync, Copy buff into bloc_a */
      /* send signal */
      while(spu_stat_out_mbox() == 0);
      spu_write_out_mbox((uint32_t)READY);




      printf("__SPE %d - D\n",spe_num);
      /* wait for signal */
      while(spu_stat_in_mbox() == 0);
      sig = spu_read_in_mbox();
      if (sig != GO){
	perror("__SPE didn't receive a GO");
	exit(1);
      }



      /* copy received bloc from buffer */
      memcpy(bloc_a,buff_a,BLOC_SIZE*BLOC_SIZE*sizeof(t_complex));
      printf("__SPE %d - E\n",spe_num);

    }



    printf("__SPE %d attempting Save\n",spe_num);
    /* Save Result : C(i,spe_num) = bloc_c */
    mfc_put(bloc_c, abs_params.out_matrix_C + abs_params.size_P * BLOC_SIZE * i + BLOC_SIZE * spe_num,
	    BLOC_SIZE * BLOC_SIZE * sizeof(t_complex), tag, tid, rid);
    /* mfc_put(bloc_c, abs_params.out_matrix_C + abs_params.size_P * BLOC_SIZE * i , */
/* 	    BLOC_SIZE * BLOC_SIZE, tag, tid, rid); */
    mfc_write_tag_mask(tag_mask);
    res = mfc_read_tag_status_all();

    //}// FIN TEST
  }
  






  /* DMA Transfer 2 : GET Matrix A */
  /* mfc_get(in_A, abs_params.in_matrix_A, abs_params.size_M * abs_params.size_N * sizeof(t_complex), tag, tid, rid); */
/*   mfc_get(in_B, abs_params.in_matrix_B, abs_params.size_N * abs_params.size_P * sizeof(t_complex), tag, tid, rid); */
/*   mfc_get(out_C, abs_params.out_matrix_C, abs_params.size_M * abs_params.size_P * sizeof(t_complex), tag, tid, rid); */
/*   mfc_write_tag_mask(tag_mask); */
/*   mfc_read_tag_status_all(); */
  
  /* Calcul de la multiplication */
  //calculation(abs_params.size_M,abs_params.size_N,abs_params.size_P, in_A, in_B, out_C);

  /* DMA Transfer 5 : PUT output data */
  /* mfc_put(out_C, abs_params.out_matrix_C,abs_params.size_M * abs_params.size_P * sizeof(t_complex), tag, tid, rid); */
/*   mfc_write_tag_mask(1<<tag); */
/*   mfc_read_tag_status_all(); */

  return 0;
}









  /*  Types vecteurs pour tout-à-l'heure ...         */
  /*  vector float *vin  = (vector float *) in_spe;  */
  /*  vector float *vout = (vector float *) out_spe; */


//// ANCIEN CODE DE CALCUL DE VALEUR ABSOLUE   ////

    /* /\* Calculate absolute values with vector operation *\/ */
/*     for (i = 0; i < abs_params.size/4; i++) { */
/*       /\* STEP1: find all values > 0 the vector vin[i] with spu_cmpgt *\/ */
      
/*       zero = spu_splats(0.0f); */
/*       moinsun = spu_splats(-1.0f); */
/*       cmp = spu_cmpgt(vin[i],zero); */
/*       neg = spu_mul(moinsun,vin[i]); */
/*       vout[i] = spu_sel(neg,vin[i],cmp); */

